package nonogram;

import java.util.ArrayList;
import java.util.List;

public class Group {
	private int size = 0;
	protected boolean complete = false;
	private boolean justCompleted = false;

	// Every group needs a list of subgroups.
	List<SubGroup> subGroups = new ArrayList<>();

	public Group(int val) {
		setSize(val);
	}

	public int getSize() {
		return size;
	}

	public void setSize(int val) {
		size = val;
	}

	public boolean getComplete() {
		this.updateComplete();
		return complete;
	}
	
	public boolean getJustCompleted() {
		return justCompleted;
	}
	
	public void setNotJustCompleted() {
		justCompleted = false;
	}

	public void updateComplete() {
		if (complete) {
			return;
		}
		
		if (this.getSize() == this.getSubGroupSize()) {
			complete = true;
			justCompleted = true;
		}
	}
	
	public boolean inProgress() {
		return (subGroups.size() != 0);
	}
	
	public boolean inProgressOrComplete() {
		return this.inProgress() || complete;
	}
	
	public int getStart() {
		if (!this.inProgress()) {
			return -1;
		}
		
		return subGroups.get(0).getStart();
	}
	
	public int getEnd() {
		if (!this.inProgress()) {
			return -1;
		}
		
		return subGroups.get(subGroups.size()-1).getEnd();
	}
	
	public int getSubGroupSize() {
		if (!this.inProgress()) {
			return -1;
		}
		
		return this.getEnd() - this.getStart();
	}
	
	/**
	 * The first safe cell to the left not connected to this group.
	 * @return
	 */
	public int getSafeBeforeStart() {
		if (!this.inProgress()) {
			return -1;
		}
		
		return this.getStart() - 2;
	}
	
	/**
	 * The first safe cell to the right not connected to this group.
	 * @return
	 */
	public int getSafeAfterEnd() {
		if (!this.inProgress()) {
			return -1;
		}
		
		return this.getEnd() + 1;
	}

	public void addSubGroup(SubGroup sub) {
		// The subgroup added will be non-overlapping.

		// See where this subgroup fits before to add.
		for (int i = 0; i < subGroups.size(); i++) {
			if (sub.getStart() < subGroups.get(i).getStart()) {
				subGroups.add(i, sub);
				return;
			}
		}
		
		// Subgroup should be added to end (or is the first one added).
		subGroups.add(sub);
	}

	/**
	 * 
	 * @param first
	 *            The leftmost/uppermost subgroup trying to fit.
	 * @param last
	 *            The rightmost/lowermost subgroup trying to fit.
	 * @return
	 */
	public boolean subGroupsFit(SubGroup first, SubGroup last) {
		if (complete) {
			return false;
		}
		
		if (subGroups.size() == 0) {
			return checkSubGroupsFit(first, last);
		}

//		SubGroupSizeComparator comparator = new SubGroupSizeComparator();

		SubGroup currentFirst = subGroups.get(0);
		SubGroup currentLast = subGroups.get(subGroups.size() - 1);

		SubGroup actualFirst;
		SubGroup actualLast;

		if (currentFirst.getStart() < first.getStart()) {
			actualFirst = currentFirst;
		}
		else {
			actualFirst = first;
		}

		if (currentLast.getEnd() > last.getEnd()) {
			actualLast = currentLast;
		}
		else {
			actualLast = last;
		}

		return checkSubGroupsFit(actualFirst, actualLast);
	}

	private boolean checkSubGroupsFit(SubGroup first, SubGroup last) {
		return (last.getEnd() - first.getStart() <= this.getSize());
	}
	
	public List<Integer> mergeSubGroups() {
		List<Integer> changedIndices = new ArrayList<>();
		int size = subGroups.size();
		
		if (size == 0) {
			return changedIndices;
		}
		
		if (size > 1) {
			SubGroup first;
			SubGroup second;
			
			for (int i = 0; i < size-1; i++) {
				first = subGroups.get(i);
				second = subGroups.get(i+1);
				
				for (int j = first.getEnd(); j < second.getStart(); j++) {
					changedIndices.add(j);
				}
			}
			
			int newStart = subGroups.get(0).getStart();
			int newEnd = subGroups.get(size-1).getEnd();
			subGroups.clear();
			subGroups.add(new SubGroup(newStart, newEnd));
		}
		
		// Exactly one subgroup since we returned if there were none/
		if (this.getSize() == subGroups.get(0).getSize()) {
			this.updateComplete();
		}
		
		return changedIndices;
	}
	
	public void updateSubGroup(int index, int start, int end) {
		SubGroup sub = subGroups.get(index);
		sub.setStart(start);
		sub.setEnd(end);
	}
	
	@Override
	public String toString() {
		String str = "";
		
		str += "[";
		
		for (SubGroup sub : subGroups) {
			str += sub;
			if (sub != subGroups.get(subGroups.size()-1)) {
				str += ", ";
			}
		}
		
		str += "]";
		return str;
	}
}
